using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Gko.Utils
{
	/// <summary>
	/// Equality comparer wrapper on method
	/// </summary>
	/// <typeparam name="TValue">The type of the value.</typeparam>
	public class EqualityComparerWrapper<TValue> : IEqualityComparer<TValue>
	{
		private readonly Func<TValue, TValue, bool> comparer;
		private readonly Func<TValue, int> hasher;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/> class.
		/// </summary>
		/// <param name="comparer">Function that compares two objects.</param>
		/// <param name="hasher">Function that returns hash code.</param>
		/// <exception cref="ArgumentNullException"><c>comparer</c> or <c>hasher</c> is null.</exception>
		public EqualityComparerWrapper(Func<TValue, TValue, bool> comparer, Func<TValue, int> hasher)
		{
			if (comparer == null) {
				throw new ArgumentNullException("comparer");
			}
			if (hasher == null) {
				throw new ArgumentNullException("hasher");
			}
			this.comparer = comparer;
			this.hasher = hasher;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/> class.
		/// </summary>
		/// <param name="comparer">The comparer.</param>
		/// <exception cref="ArgumentNullException"><c>comparer</c> is null.</exception>
		public EqualityComparerWrapper(Func<TValue, TValue, bool> comparer)
		{
			if (comparer == null) {
				throw new ArgumentNullException("comparer");
			}
			this.comparer = comparer;
			this.hasher = obj => obj.GetHashCode();
		}

		#region Implementation of IEqualityComparer<TValue>

		public bool Equals(TValue x, TValue y)
		{
			return this.comparer(x, y);
		}

		public int GetHashCode(TValue obj)
		{
			return this.hasher(obj);
		}

		#endregion
	}

	/// <summary>
	/// Static helper functions for creating <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/>
	/// </summary>
	public static class EqualityComparerWrapper
	{
		/// <summary>
		/// Wraps the specified comparer with hash function.
		/// </summary>
		/// <typeparam name="TValue">The type of the value.</typeparam>
		/// <param name="comparer">Function that compares two objects.</param>
		/// <param name="hasher">Function that returns hash code.</param>
		/// <returns>New instance of the <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/> class</returns>
		public static IEqualityComparer<TValue> Wrap<TValue>(Func<TValue, TValue, bool> comparer, Func<TValue, int> hasher)
		{
			return new EqualityComparerWrapper<TValue>(comparer, hasher);
		}

		/// <summary>
		/// Wraps the specified comparer.
		/// </summary>
		/// <typeparam name="TValue">The type of the value.</typeparam>
		/// <param name="comparer">Function that compares two objects.</param>
		/// <returns>New instance of the <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/> class</returns>
		public static IEqualityComparer<TValue> Wrap<TValue>(Func<TValue, TValue, bool> comparer)
		{
			return new EqualityComparerWrapper<TValue>(comparer);
		}

		/// <summary>
		/// Wraps equality comparer based on selector.
		/// </summary>
		/// <typeparam name="TValue">The type of the value.</typeparam>
		/// <typeparam name="TOther">The type of the other.</typeparam>
		/// <param name="selector">The selector.</param>
		/// <returns>New instance of the <see cref="T:Gko.Utils.EqualityComparerWrapper`1"/> class</returns>
		/// <exception cref="ArgumentNullException"><c>selector</c> is null.</exception>
		public static IEqualityComparer<TValue> Wrap<TValue, TOther>(Func<TValue, TOther> selector)
		{
			if (selector == null) {
				throw new ArgumentNullException("selector");
			}
			return new EqualityComparerWrapper<TValue>((a, b) => selector(a).Equals(selector(b)), a => selector(a).GetHashCode());
		}
	}
}